ㄟ害~剩下一天
先用POSTMAN測試http://localhost:8080/允許
測試http://localhost:8080/api/hello/helloworld/rawdata就沒有開放
修改ApplictionConfig程式碼
package com.tzu.config;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
import com.mysql.cj.jdbc.MysqlDataSource;
import com.tzu.beans.HelloBean;
import com.tzu.beans.HelloProxy;
import com.tzu.beans.IHello;
import com.tzu.beans.TWHello;
import com.tzu.domain.ApiKeyRepository;
import com.tzu.filter.ApiKeyFilter;
//透過方法生產Bean物件 註冊到Spring容器去
@EnableWebSecurity
@Configuration
public class ApplictionConfig {
//Attribute 使用spEL ${}
//@Value標註取出預設組態application.properties設定項目
@Value("${spring.datasource.url}")
private String url;
@Value("${spring.datasource.username}")
private String userName;
@Value("${spring.datasource.password}")
private String password;
public ApplictionConfig() {
System.out.println("Configuration Bean配置了");
}
//生產一個HelloBean物件
@Bean(name="hellonean")
public HelloBean getHelloBean() {
System.out.println("Hello Bean產生了");
//建構HelloBean
HelloBean hello=new HelloBean();
return hello;
}
@Bean
public TWHello getTWHello() {
System.out.println("TW Hello Bean產生了");
//建構HelloBean
TWHello hello=new TWHello();
return hello;
}
//參數使用定義Bean alias Name 注入依賴 隨著窗口物件注入到對方去 進行反轉物件注入
@Bean
public HelloProxy getHelloProxy(TWHello bean) {
var helloProxy=new HelloProxy(bean);
return helloProxy;
}
//產生一個DataSource 是共用的物件(連接物件工廠 整個應用系統工廠只要一個即可)
@Bean
@Scope("singleton")
public DataSource createDataSource() {
System.out.println("Datasource:"+this.url);
//建構MySQLDataSource
MysqlDataSource datasource=new MysqlDataSource();
//配置要件 URL/User name/password
datasource.setUrl(url);
datasource.setUser(userName);
datasource.setPassword(password);
//Driver 會進行內部使用
return datasource;
}
//生產JdbcTemplate元件(Spring Bean)
//透過IoC注入控制反轉 注入DataSource物件
@Bean //生命週期每一個注入 產生一個體
public JdbcTemplate createJdbcTemplate(DataSource datasource) {
//建構JdbcTemplate物件
System.out.println("JdbcTemplate 注入的DataSource:"+datasource.toString());
JdbcTemplate template=new JdbcTemplate();
template.setDataSource(datasource); //Property Injection屬性注入依賴物件
return template;
}
//註冊Filter bean 進入Spring Container
//使用IoC 注入控制反轉 注入需要JpaRepository
@Bean
public FilterRegistrationBean<ApiKeyFilter> apikeyFilter(ApiKeyRepository reposit) {
System.out.println("註冊Filter攔截器 ApiKeyFilter..."+reposit);
FilterRegistrationBean<ApiKeyFilter> register=
new FilterRegistrationBean<>();
var filter=new ApiKeyFilter();
filter.setReposit(reposit); //透過Property Injection
register.setFilter(filter); //註冊Filter
//設定url pattern
register.addUrlPatterns("/api/customers/*"); //設定Filter UrlPattern 攔截起來的端點
return register;
}
//產生Web Security Config
//使用注入控制反轉 (IoC)
@Bean
public SecurityFilterChain configSecurity(HttpSecurity http) {
System.out.println(http.toString());
//設定CSRF 安全性 產生403 status
SecurityFilterChain chain=null;
try {
http.csrf()
.and();
http.formLogin().loginPage("/mylogin")
.and()
.securityMatcher("/**")
.authorizeHttpRequests()
.requestMatchers("/","/mylogin","/api/hello/**","/api/member/**").permitAll() //允許匿名存取(只允許網站入口-首頁/燈入頁面
.requestMatchers("/css/**","/html/**","/js/**","/ui/**").permitAll(); //針對static file允續匿名存取
//.anyRequest()
//.authenticated(); //調用到登入頁面 登入頁面並沒有匿名存取 又要求到自己一次....
//... 匿名存取網站 通通可以通行
//透過HttpSecurity Builder(工廠)生產出SecurityFilterChain
chain=http.build();
} catch (Exception e) {
// TODO Auto-generated catch block
System.out.println(e.getMessage());
}
return chain;
}
}
測試http://localhost:8080/api/hello/helloworld/rawdata
就有開放
你的 Spring Boot 配置類(ApplictionConfig
)有一些複製貼上的問題,重複定義了相同的類。請將其整理成如下的配置:
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
import com.mysql.cj.jdbc.MysqlDataSource;
import com.tzu.beans.HelloBean;
import com.tzu.beans.HelloProxy;
import com.tzu.beans.TWHello;
import com.tzu.domain.ApiKeyRepository;
import com.tzu.filter.ApiKeyFilter;
import javax.sql.DataSource;
@EnableWebSecurity
@Configuration
public class ApplicationConfig {
@Value("${spring.datasource.url}")
private String url;
@Value("${spring.datasource.username}")
private String userName;
@Value("${spring.datasource.password}")
private String password;
@Bean(name = "helloBean")
public HelloBean getHelloBean() {
return new HelloBean();
}
@Bean
public TWHello getTWHello() {
return new TWHello();
}
@Bean
public HelloProxy getHelloProxy(TWHello bean) {
return new HelloProxy(bean);
}
@Bean
@Scope("singleton")
public DataSource createDataSource() {
MysqlDataSource dataSource = new MysqlDataSource();
dataSource.setUrl(url);
dataSource.setUser(userName);
dataSource.setPassword(password);
return dataSource;
}
@Bean
public JdbcTemplate createJdbcTemplate(DataSource dataSource) {
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setDataSource(dataSource);
return jdbcTemplate;
}
@Bean
public FilterRegistrationBean<ApiKeyFilter> apikeyFilter(ApiKeyRepository repository) {
FilterRegistrationBean<ApiKeyFilter> registrationBean = new FilterRegistrationBean<>();
ApiKeyFilter filter = new ApiKeyFilter();
filter.setReposit(repository);
registrationBean.setFilter(filter);
registrationBean.addUrlPatterns("/api/customers/*");
return registrationBean;
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.csrf().disable() // 禁用 CSRF
.authorizeRequests()
.antMatchers("/", "/mylogin", "/css/**", "/html/**", "/js/**", "/ui/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin().loginPage("/mylogin");
return http.build();
}
}
這樣的配置更整潔,並消除了重複定義的問題。請確保各個依賴的類都在相應的包下並被正確引入。
依角色建立AppUsers.java 檔案:使用Users.Buildr 朝狀類別 工廠模式類別
package com.tzu.domain;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
public class AppUsers implements UserDetailsService{
//attribute
private String userName;
private String password;
private String[] roles;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return null;
}
}
這段Java程式碼是一個類別 AppUsers
,看起來它是實現了Spring Security框架中的 UserDetailsService
介面。這個介面通常用於處理用戶驗證和授權的相關邏輯。讓我解釋一下這段程式碼的主要部分:
package com.tzu.domain;
:這是Java程式碼的包聲明,它指定了這個類別的所屬包。
import
陳述句:這些 import
陳述句引入了所需的外部類別,包括 UserDetails
和 UserDetailsService
,它們來自Spring Security框架。它們是用於處理用戶身份驗證和授權的關鍵介面。
public class AppUsers implements UserDetailsService
:這是 AppUsers
類別的定義,它實現了 UserDetailsService
介面,這意味著該類別需要實現 loadUserByUsername
方法,以處理用戶驗證。
private String userName;
、private String password;
、private String[] roles;
:這些是類別的私有屬性,它們用於存儲用戶的名稱、密碼和角色信息。
public UserDetails loadUserByUsername(String username)
方法:這是實現 UserDetailsService
介面所必需的方法。它用於根據提供的使用者名稱(username
)查找和返回相應的用戶詳細信息(UserDetails
)。在目前的程式碼中,方法內部尚未實作,只是返回了 null
。
通常,你需要在 loadUserByUsername
方法中實作用戶驗證邏輯,包括檢查使用者名稱和密碼,以及分配角色等操作。這個程式碼片段只是一個初始框架,你需要根據你的應用程序需求來實現更具體的邏輯。
按右鍵產生get跟set
打V
顯示
package com.tzu.domain;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
public class AppUsers implements UserDetailsService{
//attribute
private String userName;
private String password;
private String[] roles;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return null;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String[] getRoles() {
return roles;
}
public void setRoles(String[] roles) {
this.roles = roles;
}
}
一個Java類,可能用於Spring Security框架中。這個類似乎是用於處理使用者認證和授權的一部分。讓我為您解釋其中的一些關鍵點:
AppUsers
類實現了 UserDetailsService
介面,這表明它是一個自定義的使用者詳細資訊服務類,用於 Spring Security 的使用者認證。
在 loadUserByUsername
方法中,您需要實現根據使用者名稱載入使用者詳細資訊的邏輯。通常,您會從資料庫或其他資料儲存中檢索使用者的使用者名稱、密碼和角色資訊,並返回一個實現了 UserDetails
介面的物件。這個物件通常包含有關使用者的認證和授權資訊。
類中包括了使用者名稱(userName
)、密碼(password
)和角色(roles
)屬性,這些屬性可能在 loadUserByUsername
方法中被設定為從資料來源中檢索到的值。
使用者名稱和密碼是使用者的認證憑據,而角色通常用於使用者的授權,以確定使用者是否有訪問某些受保護資源的許可權。
請注意,這只是一個類的一部分,要使它完全運作,您需要實現 loadUserByUsername
方法中的邏輯,並配置Spring Security以使用您的AppUsers
類來處理使用者認證和授權。通常,Spring Security需要與資料庫或其他身份驗證和授權資料來源整合,以驗證使用者並控制其訪問許可權。
修改AppUsers.java 檔案:使用Users.Buildr 朝狀類別 工廠模式類別
package com.tzu.domain;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
//封裝使用者登入驗證 OK識別物件(包含有UserName/Password/Roles)
public class AppUsers implements UserDetailsService{
//attribute
private String userName;
private String password;
private String[] roles;
//屬性注入Property Injection
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//使用Users.Buildr 朝狀類別 工廠模式類別
UserDetails userDetails=User.builder()
.password(password)
.username(username)
.roles(roles)
.build();
return userDetails;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String[] getRoles() {
return roles;
}
public void setRoles(String[] roles) {
this.roles = roles;
}
}
這段Java程式碼是一個類別 AppUsers
,它實現了Spring Security框架中的 UserDetailsService
介面,並提供了一個簡單的用戶登入驗證機制。讓我解釋一下這段程式碼的主要部分:
private String userName;
、private String password;
、private String[] roles;
:這些是類別的私有屬性,分別用於存儲用戶的名稱、密碼和角色信息。
public UserDetails loadUserByUsername(String username)
方法:這是實現 UserDetailsService
介面所必需的方法。它根據提供的使用者名稱(username
)建立一個 UserDetails
物件,其中包括使用者的名稱、密碼和角色。這個方法使用了Spring Security提供的 User.builder()
工廠模式來創建 UserDetails
物件,然後返回該物件。
public String getUserName()
、public void setUserName(String userName)
、public String getPassword()
、public void setPassword(String password)
、public String[] getRoles()
、public void setRoles(String[] roles)
:這些是用於設定和獲取 userName
、password
和 roles
屬性的 getter 和 setter 方法。
這個程式碼片段的主要功能是封裝了用戶的登入資訊,包括使用者名稱、密碼和角色,然後在 loadUserByUsername
方法中根據提供的使用者名稱返回相應的用戶詳細信息。這個類別似乎用於自訂用戶登入驗證邏輯,可以在Spring Security配置中使用。根據需要,你可以進一步擴展這個類別以滿足你的應用程序需求。
打開資料庫:新增會員資料表
CREATE TABLE `sakila`.`members` (
`username` VARCHAR(30) NOT NULL,
`password` VARCHAR(45) NOT NULL,
`roles` VARCHAR(45) NOT NULL,
PRIMARY KEY (`username`))
COMMENT = '會員資料表';
先填入一個資料
再新增一個AppUserHandler.java檔案:做RBAC
以角色為基礎的存取控制[1][2](英語:Role-based access control,RBAC),是資訊安全領域中,一種較新且廣為使用的存取控制機制,其不同於強制存取控制以及自由選定存取控制[3]直接賦予使用者權限,而是將權限賦予角色。
package com.tzu.domain;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
public class AppUserHandler implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// TODO Auto-generated method stub
return null;
}
}
這個程式碼片段是一個Java類別 AppUserHandler
,它實現了Spring Security框架中的 UserDetailsService
介面,但在 loadUserByUsername
方法中尚未實作具體的用戶驗證邏輯。讓我幫你理解這段程式碼的主要要點:
import
陳述句:這些 import
陳述句引入了Spring Security框架中所需的類別,包括 UserDetails
和 UserDetailsService
,這些是用於處理用戶身份驗證和授權的介面。
public UserDetails loadUserByUsername(String username)
方法:這是實現 UserDetailsService
介面所必需的方法。然而,在目前的程式碼中,方法內部只有一個註解 // TODO Auto-generated method stub
,表示該方法尚未實作。
這個 AppUserHandler
類別似乎是設計用來處理用戶身份驗證的部分,但你需要實作 loadUserByUsername
方法,以根據提供的使用者名稱(username
)來查找並返回相應的用戶詳細信息。通常,這個方法會檢查用戶名稱和密碼,然後返回一個實現 UserDetails
介面的物件,其中包含用戶的資訊和角色。這個類別的實際用途和邏輯需要根據你的應用程序需求進一步實作。
再新增一個MemberIdentity.java檔案:
package com.tzu.domain;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;
@Entity
@Table(name="members")
public class MemberIdentity {
//應對欄位的描述
private String userName;
private String password;
private String roles;
}
右鍵做get跟set
都打V
加入應對欄位的描述
package com.tzu.domain;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
@Entity
@Table(name="members")
public class MemberIdentity {
//應對欄位的描述
@Id
@Column(name="username")
private String userName;
@Column(name="password")
private String password;
@Column(name="roles")
private String roles;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getRoles() {
return roles;
}
public void setRoles(String roles) {
this.roles = roles;
}
}
這段程式碼是一個Java類別 MemberIdentity
,它使用Jakarta Persistence API(曾稱為Java Persistence API,JPA)標記為實體,並用於映射到資料庫表格。讓我解釋一下這段程式碼的主要要點:
@Entity
:這是JPA的標記(Annotation),表示這個類別是一個實體,可以被映射到資料庫中的表格。
@Table(name="members")
:這是另一個JPA標記,指定了將這個實體映射到名稱為 "members" 的資料庫表格。
@Id
:這是JPA的標記,指定 userName
屬性是這個實體的主鍵,也就是資料庫表格的主索引鍵。
@Column(name="username")
、@Column(name="password")
、@Column(name="roles")
:這些是用來對應Java類別的屬性到資料庫表格欄位的JPA標記。它們指定了每個屬性的名稱,這些名稱與資料庫表格的欄位名稱相對應。
private String userName;
、private String password;
、private String roles;
:這些是類別的私有屬性,分別代表了資料庫表格中的欄位,例如使用者名稱、密碼和角色。
public
getter 和 setter 方法:這些方法用於設定和獲取 userName
、password
和 roles
屬性的值,這是將Java物件的屬性映射到資料庫表格欄位的方式。
總結來說,這個程式碼片段定義了一個JPA實體 MemberIdentity
,並設定了它的資料庫表格映射,以便在應用程序中使用該實體來對應資料庫中的成員(會員)記錄。
再新增一個MemberJpaRepository.java檔案:
package com.tzu.domain;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface MemberJpaRepository extends JpaRepository<MemberIdentity,String>{
public MemberIdentity findByUserName(String userName);
}
這段程式碼是一個Spring Data JPA的倉庫介面 MemberJpaRepository
,它用於對應到 MemberIdentity
實體類別,並提供了資料存取的方法。讓我解釋一下這段程式碼的主要要點:
import
陳述句:這些 import
陳述句引入了Spring框架相關的類別,包括 JpaRepository
和 Repository
。
@Repository
:這是一個Spring框架的註解,它將這個介面標記為一個Spring的存儲庫(repository),用於提供資料存取的功能。
public interface MemberJpaRepository extends JpaRepository<MemberIdentity, String>
:這個介面宣告了 MemberJpaRepository
,並繼承了 JpaRepository
介面,這表示它將提供一組預設的CRUD(建立、讀取、更新、刪除)操作,針對 MemberIdentity
實體,使用 String
作為主鍵的資料型別。
public MemberIdentity findByUserName(String userName)
:這是一個自訂的查詢方法,透過 findByUserName
方法名稱來指定,它將根據使用者名稱(userName
)來查找成員(會員)記錄。Spring Data JPA會根據方法名稱的約定自動生成查詢。
這個介面 MemberJpaRepository
提供了對 MemberIdentity
實體的常見資料存取操作,並且你可以使用它來輕鬆地執行資料庫查詢和更新操作,而不必手動編寫SQL查詢語句。這有助於簡化資料存取層的開發工作。
再加入一個MemberService檔案
package com.tzu.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import com.tzu.domain.MemberJpaRepository;
@RestController
public class MemberService {
//注入依賴的Jpa
@Autowired
private MemberJpaRepository memberRepo;
//使用登入驗證作業
@GetMapping(path="/api/member/uservalid/{username}/{pwd}/rawdata",produces="application/json")
public String userVaid(@PathVariable(name="username")String userName,@PathVariable(name="pwd")String password) {
return userName+" "+memberRepo;
}
}
這段程式碼是一個Spring Boot應用程序中的REST控制器 MemberService
,它使用Spring框架和Spring Boot來處理HTTP請求。讓我解釋這段程式碼的主要要點:
import
陳述句:這些 import
陳述句引入了Spring框架和Spring Boot相關的類別,包括 Autowired
、GetMapping
、RestController
,以及 MemberJpaRepository
。
@RestController
:這是一個Spring Boot註解,標記這個類別為一個REST控制器,它將處理HTTP請求並返回JSON格式的響應。
@Autowired
:這是Spring框架的註解,用於進行依賴注入。在這個情況下,它將 MemberJpaRepository
類別注入到 memberRepo
屬性中,以便在這個控制器中使用。
@GetMapping
:這是一個Spring Boot註解,指定了這個方法應該處理HTTP GET請求。path
屬性指定了請求的路徑,produces
屬性指定了回傳的資料格式。
public String userValid(@PathVariable(name="username") String userName, @PathVariable(name="pwd") String password)
:這是一個HTTP GET請求的處理方法,它接受兩個路徑變數 username
和 pwd
,分別代表使用者名稱和密碼。在這個方法中,它返回了使用者名稱和 memberRepo
的字串表示,但是似乎還未執行實際的登入驗證邏輯。
總之,這個控制器 MemberService
負責處理HTTP GET請求,並透過注入的 MemberJpaRepository
來處理會員資料的操作,但在目前的程式碼中,登入驗證邏輯尚未被實作,所以還需要進一步開發以實現登入驗證的功能。
再修改ApplictionConfig.java
package com.tzu.config;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
import com.mysql.cj.jdbc.MysqlDataSource;
import com.tzu.beans.HelloBean;
import com.tzu.beans.HelloProxy;
import com.tzu.beans.IHello;
import com.tzu.beans.TWHello;
import com.tzu.domain.ApiKeyRepository;
import com.tzu.filter.ApiKeyFilter;
//透過方法生產Bean物件 註冊到Spring容器去
@EnableWebSecurity
@Configuration
public class ApplictionConfig {
//Attribute 使用spEL ${}
//@Value標註取出預設組態application.properties設定項目
@Value("${spring.datasource.url}")
private String url;
@Value("${spring.datasource.username}")
private String userName;
@Value("${spring.datasource.password}")
private String password;
public ApplictionConfig() {
System.out.println("Configuration Bean配置了");
}
//生產一個HelloBean物件
@Bean(name="hellonean")
public HelloBean getHelloBean() {
System.out.println("Hello Bean產生了");
//建構HelloBean
HelloBean hello=new HelloBean();
return hello;
}
@Bean
public TWHello getTWHello() {
System.out.println("TW Hello Bean產生了");
//建構HelloBean
TWHello hello=new TWHello();
return hello;
}
//參數使用定義Bean alias Name 注入依賴 隨著窗口物件注入到對方去 進行反轉物件注入
@Bean
public HelloProxy getHelloProxy(TWHello bean) {
var helloProxy=new HelloProxy(bean);
return helloProxy;
}
//產生一個DataSource 是共用的物件(連接物件工廠 整個應用系統工廠只要一個即可)
@Bean
@Scope("singleton")
public DataSource createDataSource() {
System.out.println("Datasource:"+this.url);
//建構MySQLDataSource
MysqlDataSource datasource=new MysqlDataSource();
//配置要件 URL/User name/password
datasource.setUrl(url);
datasource.setUser(userName);
datasource.setPassword(password);
//Driver 會進行內部使用
return datasource;
}
//生產JdbcTemplate元件(Spring Bean)
//透過IoC注入控制反轉 注入DataSource物件
@Bean //生命週期每一個注入 產生一個體
public JdbcTemplate createJdbcTemplate(DataSource datasource) {
//建構JdbcTemplate物件
System.out.println("JdbcTemplate 注入的DataSource:"+datasource.toString());
JdbcTemplate template=new JdbcTemplate();
template.setDataSource(datasource); //Property Injection屬性注入依賴物件
return template;
}
//註冊Filter bean 進入Spring Container
//使用IoC 注入控制反轉 注入需要JpaRepository
@Bean
public FilterRegistrationBean<ApiKeyFilter> apikeyFilter(ApiKeyRepository reposit) {
System.out.println("註冊Filter攔截器 ApiKeyFilter..."+reposit);
FilterRegistrationBean<ApiKeyFilter> register=
new FilterRegistrationBean<>();
var filter=new ApiKeyFilter();
filter.setReposit(reposit); //透過Property Injection
register.setFilter(filter); //註冊Filter
//設定url pattern
register.addUrlPatterns("/api/customers/*"); //設定Filter UrlPattern 攔截起來的端點
return register;
}
//產生Web Security Config
//使用注入控制反轉 (IoC)
@Bean
public SecurityFilterChain configSecurity(HttpSecurity http) {
System.out.println(http.toString());
//設定CSRF 安全性 產生403 status
SecurityFilterChain chain=null;
try {
http.csrf()
.and();
http.formLogin().loginPage("/mylogin")
.and()
.securityMatcher("/**")
.authorizeHttpRequests()
.requestMatchers("/","/mylogin","/api/hello/**","/api/member/**").permitAll() //允許匿名存取(只允許網站入口-首頁/燈入頁面
.requestMatchers("/css/**","/html/**","/js/**","/ui/**").permitAll(); //針對static file允續匿名存取
//.anyRequest()
//.authenticated(); //調用到登入頁面 登入頁面並沒有匿名存取 又要求到自己一次....
//... 匿名存取網站 通通可以通行
//透過HttpSecurity Builder(工廠)生產出SecurityFilterChain
chain=http.build();
} catch (Exception e) {
// TODO Auto-generated catch block
System.out.println(e.getMessage());
}
return chain;
}
}
這段程式碼是一個Spring Boot應用程式的配置類別 ApplictionConfig
,負責配置應用程式中的Spring Beans以及安全性設定。以下是這段程式碼的主要要點:
@Configuration
:這是Spring Framework的註解,標記這個類別為配置類別,告訴Spring容器這個類別包含了Bean的定義。
@Value
:這是Spring框架的註解,用於注入屬性值,通常是從 application.properties
文件中讀取的屬性值。
@Bean
:這是Spring Framework的註解,用於定義Bean的方法。在這個類別中,有多個 @Bean
方法,用於建立不同的Bean物件。
注入依賴:透過 @Autowired
,你可以將依賴的Bean注入到其他Bean中。例如,createJdbcTemplate
方法注入了一個 DataSource
物件,並用於建立 JdbcTemplate
Bean。
註冊Filter:apikeyFilter
方法註冊了一個名為 ApiKeyFilter
的Filter,並指定了要攔截的URL模式。
安全性配置:configSecurity
方法配置了應用程式的安全性,包括CSRF安全性、登入頁面、以及哪些URL可以匿名存取。這部分看起來是針對Spring Security的配置。
總之,這個配置類別主要負責設定Spring Beans、資料庫連接、Filter以及應用程式的安全性。這些Beans和設定將被Spring容器管理,以協助你的應用程式運作。然而,還有一些錯誤在程式碼中,例如 @EnableWebSecurity
和 http.securityMatcher
應該是 http.antMatcher
。請確保這些錯誤被修正以使應用程式正確運行。
用POSTMAN測試http://localhost:8080/api/member/uservalid/eric/1111/rawdata
修改AppUserHandler 程式碼
package com.tzu.domain;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
//進行會員資料查詢 借助屬性注入JPA
public class AppUserHandler implements UserDetailsService {
private MemberJpaRepository memberRepo;
//透過屬性注入setXxxx
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
MemberIdentity member=memberRepo.findByUserName(username);
UserDetails userDetails=null;
//判斷是否有找到
if(member==null) {
throw new UsernameNotFoundException("查無該使用者帳戶");
}else {
//產生回應的UserDetails
String[] roles=member.getRoles().split(",");
userDetails=User.builder()
.username(member.getUserName())
.password(member.getPassword())
.roles(roles)
.build();
}
return userDetails;
}
public void setMemberRepo(MemberJpaRepository memberRepo) {
this.memberRepo = memberRepo;
}
}
這段程式碼是一個實作Spring Security的 UserDetailsService
介面的類別 AppUserHandler
,它負責處理使用者登入驗證,並借助屬性注入 MemberJpaRepository
以查詢會員資料。以下是這段程式碼的主要要點:
UserDetailsService
介面:這個類別實作了Spring Security的 UserDetailsService
介面,該介面是Spring Security用於查詢使用者資料的一個關鍵部分。實作這個介面需要實現 loadUserByUsername
方法。
MemberJpaRepository
:這是一個使用了JPA的資料存取介面,用於查詢會員資料。在這個程式碼中, memberRepo
屬性透過屬性注入(setter注入)方式被設定。
loadUserByUsername
方法:這是實作 UserDetailsService
介面所必須的方法。當使用者嘗試登入時,Spring Security會呼叫這個方法,傳遞使用者提供的使用者名稱(username
)。在這個方法中,程式碼試圖根據使用者名稱從資料庫中查詢會員資料。
memberRepo.findByUserName(username)
:這個方法呼叫了 memberRepo
的 findByUserName
方法,以根據使用者名稱查詢會員資料。如果找到了對應的會員,則會建立一個 UserDetails
物件,其中包括使用者名稱、密碼和角色資訊。
使用者未找到:如果找不到對應的會員資料,則會拋出 UsernameNotFoundException
例外,表示未找到該使用者帳戶。
使用者找到:如果找到了會員資料,則會建立一個 UserDetails
物件,其中包括使用者名稱、密碼和角色資訊,然後將該物件返回,以供Spring Security使用。
總之,這個 AppUserHandler
類別實現了 UserDetailsService
介面,並透過 MemberJpaRepository
查詢會員資料,以進行使用者的登入驗證。
修改MemberService檔案
package com.tzu.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import com.tzu.domain.AppUserHandler;
import com.tzu.domain.MemberJpaRepository;
@RestController
public class MemberService {
//注入依賴的Jpa
@Autowired
private MemberJpaRepository memberRepo;
//使用登入驗證作業
@GetMapping(path="/api/member/uservalid/{username}/{pwd}/rawdata",produces="application/json")
public UserDetails userVaid(@PathVariable(name="username")String userName,@PathVariable(name="pwd")String password) {
//建構自訂UserDetailsService
AppUserHandler userService=new AppUserHandler();
//注入Jap
userService.setMemberRepo(memberRepo);
//透過服務取出UserDetails物件
UserDetails userDetails=userService.loadUserByUsername(userName);
return userDetails;
}
}
這段程式碼是一個Spring Boot應用程式的REST控制器 MemberService
,它用於處理HTTP請求並執行使用者的登入驗證。以下是這段程式碼的主要要點:
@RestController
:這是Spring Boot的註解,標記這個類別為一個REST控制器,它會處理HTTP請求並返回JSON格式的響應。
@Autowired
:這是Spring框架的註解,用於進行依賴注入。在這個情況下,它將 MemberJpaRepository
類別注入到 memberRepo
屬性中,以便在這個控制器中使用。
@GetMapping
:這是Spring Boot的註解,指定了這個方法應該處理HTTP GET請求。path
屬性指定了請求的路徑,produces
屬性指定了回傳的資料格式。
userValid
方法:這是一個HTTP GET請求的處理方法,它接受兩個路徑變數 username
和 pwd
,分別代表使用者名稱和密碼。在這個方法中,它建立了一個 AppUserHandler
物件,然後注入了 memberRepo
依賴,接著呼叫 loadUserByUsername
方法進行使用者的登入驗證。最後,它將回傳的 UserDetails
物件作為JSON格式的回應。
總之,這個控制器 MemberService
負責處理HTTP GET請求,並透過 AppUserHandler
執行使用者的登入驗證,並返回使用者資料的詳細資訊,以JSON格式回應。這是一個簡單的範例,實際上,通常會使用Spring Security來處理使用者的登入驗證,而不是手動建立 AppUserHandler
物件。
目前資料庫有資料有兩個角色IT跟ACCT
用POSTMAN測試http://localhost:8080/api/member/uservalid/eric/1111/rawdata
被驗證通過/驗證通過管理員
再增加一個AppPrincipal檔案
package com.tzu.domain;
import org.springframework.security.core.AuthenticatedPrincipal;
//結合一個特定的使用者UserDetails 注入相對的UserName
public class AppPrincipal implements AuthenticatedPrincipal{
//Data Field
private String name;
//建構子注入
public AppPrincipal(String name) {
this.name=name;
}
@Override
public String getName() {
// TODO Auto-generated method stub
return name;
}
}
這段程式碼定義了一個名為 AppPrincipal
的類別,它實作了Spring Security的 AuthenticatedPrincipal
介面。這個類別用於代表一個特定使用者,提供使用者的名稱資訊。以下是這段程式碼的主要要點:
AuthenticatedPrincipal
介面:這是Spring Security的介面,代表一個已驗證的主體(使用者)。 AppPrincipal
類別實作了這個介面,所以它可以代表一個已驗證的使用者。
name
屬性:這是 AppPrincipal
類別的屬性,用於存儲使用者的名稱。這是在建立 AppPrincipal
物件時設定的。
建構子注入:這個類別有一個建構子,接受一個字串參數 name
,這個參數代表使用者的名稱。建立 AppPrincipal
物件時,可以將使用者名稱傳遞給建構子以初始化 name
屬性。
getName
方法:這個方法是 AuthenticatedPrincipal
介面的一部分,它返回使用者的名稱。在 AppPrincipal
類別中,這個方法實作了,並返回存儲在 name
屬性中的名稱。
總之,AppPrincipal
類別用於代表一個已驗證的使用者,提供使用者名稱資訊,以便在應用程式中使用。通常,當使用者成功登入後,Spring Security會建立一個 AppPrincipal
物件,代表該使用者,並將其傳遞到相關的部分以供應用程式使用。
再修改MemberService 檔案
package com.tzu.service;
import java.util.Collection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import com.tzu.domain.AppPrincipal;
import com.tzu.domain.AppUserHandler;
import com.tzu.domain.MemberJpaRepository;
@RestController
public class MemberService {
//注入依賴的Jpa
@Autowired
private MemberJpaRepository memberRepo;
//使用登入驗證作業
@GetMapping(path="/api/member/uservalid/{username}/{pwd}/rawdata",produces="application/json")
public Collection<? extends GrantedAuthority> userVaid(@PathVariable(name="username")String userName,@PathVariable(name="pwd")String password) {
//建構自訂UserDetailsService
AppUserHandler userService=new AppUserHandler();
Collection<? extends GrantedAuthority> authorties=null;
//注入Jap
userService.setMemberRepo(memberRepo);
//透過服務取出UserDetails物件
UserDetails userDetails=userService.loadUserByUsername(userName);
//進行雙重驗證
if(userDetails==null) {
//沒有這一個會員
}else {
//驗證密碼
if(password.equals(userDetails.getPassword())) {
//2.建構Principal(當事者)
AppPrincipal principal=new AppPrincipal(userDetails.getUsername());
//3.準備授權許可證
authorties=userDetails.getAuthorities();
//4.封裝當事者與使用者 與授權令牌 進入一個貫穿應用系統Context
}else {
//偽造身分
}
}
return authorties;
}
}
這段程式碼是一個Spring Boot應用程式的REST控制器 MemberService
,它負責處理HTTP GET請求以進行使用者的登入驗證。這個控制器透過 AppUserHandler
類別執行使用者的登入驗證,並返回一個 Collection
,其中包含了授權許可證(GrantedAuthority
)。以下是這段程式碼的主要要點:
@RestController
:這是Spring Boot的註解,標記這個類別為一個REST控制器,用於處理HTTP GET請求。
@Autowired
:這是Spring框架的註解,用於進行依賴注入。在這個情況下,它將 MemberJpaRepository
類別注入到 memberRepo
屬性中,以便在這個控制器中使用。
@GetMapping
:這是Spring Boot的註解,指定了這個方法應該處理HTTP GET請求。path
屬性指定了請求的路徑,produces
屬性指定了回傳的資料格式。
userValid
方法:這是一個HTTP GET請求的處理方法,它接受兩個路徑變數 username
和 pwd
,分別代表使用者名稱和密碼。在這個方法中,它建立了一個 AppUserHandler
物件,然後注入了 memberRepo
依賴,接著呼叫 loadUserByUsername
方法進行使用者的登入驗證。
雙重驗證:在這個方法中,進行了雙重驗證。首先,它檢查 UserDetails
物件是否為 null
,如果是,表示沒有對應的會員。然後,它檢查提供的密碼是否與 UserDetails
中的密碼相符。如果密碼相符,則建立一個 AppPrincipal
物件,代表當事者,然後獲取授權許可證。
授權許可證:授權許可證是一個 Collection
,其中包含了使用者的授權資訊(例如角色)。這些授權許可證可以用於控制使用者對應用程式中不同功能的存取。
總之,這個控制器 MemberService
負責處理HTTP GET請求,進行使用者的登入驗證,並返回包含授權許可證的 Collection
。這是一個簡單的範例,實際上,通常會使用Spring Security來處理使用者的登入驗證,而不是手動建立 AppUserHandler
物件。
用POSTMAN測試http://localhost:8080/api/member/uservalid/eric/1111/rawdata
顯示角色
謝謝大家收看